/*
    FUSE: Filesystem in Userspace
    Copyright (C) 2007  Lionel Tricon <lionel.tricon@free.fr>

    This program can be distributed under the terms of the GNU GPL.
*/

#include <stdlib.h>
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <td_bdd.h>


unsigned int sid;


static int td_bdd_getattr(const char *path, struct stat *stbuf)
{
	unsigned int lid;
	char *npath;
	td_row *tr;
	
	printf ("getattr : %s\n", path);
	
	memset(stbuf, 0, sizeof(struct stat));
	
	if(strcmp(path, "/") == 0)
	{
		stbuf->st_mode = S_IFDIR | 0755;
		stbuf->st_nlink = 2;
		return (0);
	}

	/*pw = getpwnam( &(path[1]) );
	if (pw == NULL) 
		return (-ENOENT);*/

	lid = td_new_session (sid);
	npath = strdup (path + 1);
	
	tr = td_select_subbase_p (lid, npath, '/');
	if (!tr)
	{
		printf ("%s => not a subbase\n", npath);
		td_close_session (lid);
		lid = td_new_session (sid);
		tr = td_select_row_p (lid, npath, '/');
		if (!tr)
		{
			printf ("%s => not a row\n", npath);
			free (npath);
			td_close_session (lid);
			return (-ENOENT);
		}
		printf ("%s => is a row\n", npath);
		stbuf->st_mode = S_IFREG | 0444;
		stbuf->st_size = (off_t) strlen (tr->val) + 1;
	}
	else
	{
		printf ("%s => is a subbase\n", npath);
		stbuf->st_mode = S_IFDIR | 0555;
		stbuf->st_size = 1;
	}
	
	stbuf->st_nlink = 1;	
	stbuf->st_uid = getuid();
	stbuf->st_gid = getgid();
	
	td_free_row (tr);
	td_close_session (lid);
	free (npath);
	
	return (0);
}


int readdir_subs (unsigned int lid, void *buf, fuse_fill_dir_t filler)
{
	td_row *tr;
	
	tr = td_select_first_subbase (lid);
	if (!tr)
	{
		td_close_session (lid);
		return (0);
	}
	filler (buf, tr->ident, NULL, 0);
	td_free_row (tr);
	
	while ((tr = td_select_next_subbase (lid)))
	{
		filler (buf, tr->ident, NULL, 0);
		td_free_row (tr);
	}
	
	return (1);
}


int readdir_rows (unsigned int lid, void *buf, fuse_fill_dir_t filler)
{
	td_row *tr;
	
	tr = td_select_first_row (lid);
	if (!tr)
		return (0);

	filler (buf, tr->ident, NULL, 0);
	td_free_row (tr);
	
	while ((tr = td_select_next_row (lid)))
	{
		filler (buf, tr->ident, NULL, 0);
		td_free_row (tr);
	}
	
	return (1);
}


static int td_bdd_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi)
{
	td_row *tr;
	unsigned int lid, lid1;
	char *npath;
	
	printf ("readdir : %s\n", path);
	(void) offset;
	(void) fi;

	if(strcmp(path, "/") == 0) 
		lid = td_new_session (sid);
	else
	{	
		lid = td_new_session (sid);
		npath = strdup (path + 1);
		tr = td_select_subbase_p (lid, npath, '/');
		free (npath);
		if (!tr)
		{
			td_close_session (lid);
			return (-ENOTDIR);
		}	
		td_free_row (tr);
	}
	
	filler(buf, ".", NULL, 0);
	filler(buf, "..", NULL, 0);
	
	lid1 = td_new_session (lid);
	readdir_subs (lid1, buf, filler);
	td_close_session (lid1);
	
	lid1 = td_new_session (lid);
	readdir_rows (lid1, buf, filler);
	td_close_session (lid1);
	
	td_close_session (lid);

	return (0);
}


static int td_bdd_open(const char *path, struct fuse_file_info *fi)
{
	printf ("open : %s\n", path);
	/*pw = getpwnam( &(path[1]) );
	
	if (pw == NULL) 
		return (-ENOENT);

	if ((fi->flags & 3) != O_RDONLY)
		return (-EACCES);*/

	return (0);
}


static int td_bdd_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
{
	char *npath;
	unsigned int lid;
	td_row *tr;
	
	printf ("read : %s\n", path);
	
	size_t len;
	(void) fi;

	/*pw = getpwnam( &(path[1]) );
	if (pw == NULL)
		return (-ENOENT);*/

	npath = strdup (path + 1);
	lid = td_new_session (sid);
	tr = td_select_row_p (lid, npath, '/');
	
	free (npath);
	td_close_session (lid);
	
	if (!tr)
		return (-ENOENT);
	
	
	len = (size_t) strlen (tr->val) + 1;
	if (offset < len)
	{
		if (offset + size > len)
			size = len - offset;
		memcpy (buf, tr->val + offset, size);
		buf[size - 1] = '\n';
		
		td_free_row (tr);
		
		return (size);
	}
	
	td_free_row (tr);

	return (0);
}


static struct fuse_operations td_bdd_oper = {
    .getattr	= td_bdd_getattr,
    .readdir	= td_bdd_readdir,
    .open	= td_bdd_open,
    .read	= td_bdd_read,
};


int main(int argc, char *argv[])
{
	int val;
	
	td_init (".");
	sid = td_open_base ("channels", NO_CASE_SENS, O_AUTO);
	
	val = fuse_main(argc, argv, &td_bdd_oper);
	td_clean ();
	
	return (val);
}

